package util.level;

import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;
import editor.LevelEditor;
import util.collision.SpaceHash;
import util.instance.Instance;
import util.instance.InstanceBag;

/**
 * The room is the heart of any game. This class contains what is necessary
 * to maintain instances, load levels, render views, and more.
 * @author Logan
 */
public abstract class Room extends BasicGameState
{
	public static final int ID=1;
	
	//Room settings and instance containers
	private int roomWidth=640,roomHeight=480;
	private InstanceBag instances;
	private SpaceHash spaceHash;
	
	//Tile and background variables
	private TileGroup backgroundGroup,foregroundGroup;
	private Color color=new Color(Color.white);
	
	//View variables
	private Instance viewObject=null;
	private int viewWidth=320;
	private int viewHeight=240;
	private int portWidth=640;
	private int portHeight=480;
	private int viewXOffset=0;
	private int viewYOffset=0;
	
	//Level editor
	private LevelEditor editor;

	public void init(GameContainer gc,StateBasedGame sbg) throws SlickException
	{
		spaceHash=new SpaceHash(roomWidth,roomHeight);
		instances=new InstanceBag();
		
		portHeight=gc.getHeight();
		portWidth=gc.getWidth();
		viewHeight=portHeight;
		viewWidth=portWidth;
		
		editor=new LevelEditor();
	}

	public void render(GameContainer gc,StateBasedGame sbg,Graphics g) throws SlickException
	{	
		//View code
		g.scale(portWidth/viewWidth,portHeight/viewHeight);
		if(viewObject!=null)
		{
			int xtranslate=Math.min(Math.max((int)viewObject.getX()+viewXOffset-viewWidth/2,0),roomWidth-viewWidth);
			int ytranslate=Math.min(Math.max((int)viewObject.getY()+viewYOffset-viewHeight/2,0),roomHeight-viewHeight);
			g.translate(-xtranslate,-ytranslate);
		}
		g.setClip(0,0,portWidth,portHeight);
		
		//Render room
		g.setColor(color);
		g.fillRect(0,0,roomWidth,roomHeight);
		if(backgroundGroup!=null)
			backgroundGroup.render(g);
		instances.render(gc,sbg,g);
		if(foregroundGroup!=null)
			foregroundGroup.render(g);
		
		//Reset changes by view code
		g.resetTransform();
		g.clearClip();
		
		//Draw level editor gui
		editor.update(gc,g,32,32);
	}

	public void update(GameContainer gc,StateBasedGame sbg,int delta) throws SlickException
	{
		instances.update(gc,sbg,this);
		instances.endUpdate(gc,sbg,this);
		instances.prune(gc,sbg,this);
		instances.refresh(gc,sbg,this);
	}
	
	/**
	 * Gets the spaceHash of the room.
	 * @return the current room's spaceHash
	 */
	public SpaceHash getSpaceHash()
	{
		return spaceHash;
	}
	
	/**
	 * Gets the instanceBag of the room. Useful for instance creation within instances.
	 * @return the current room's instanceBag.
	 */
	public InstanceBag getBag()
	{
		return instances;
	}
	
	/**
	 * The width of the room, not the screen or the view.
	 * @return the width of the room
	 */
	public int getWidth()
	{
		return roomWidth;
	}
	
	/**
	 * The height of the room, not the screen or the view.
	 * @return the height of the room
	 */
	public int getHeight()
	{
		return roomHeight;
	}
	
	/**
	 * Changes the level, but not the current state.
	 * @param levelLocation - relative file path to the level data.
	 */
	public void changeLevel(String levelLocation)
	{
		try
		{
			//Load a new file and set the room to display the new tiles and background
			LevelLoader loader=new LevelLoader(levelLocation);
			roomWidth=loader.getWidth();
			roomHeight=loader.getHeight();
			backgroundGroup=loader.getBackgroundTiles();
			foregroundGroup=loader.getForegroundTiles();
			//color=loader.getColor(); not yet fully implemented in LevelLoader
			
			//Remove everything from the instance bag and add the new objects
			instances.removeAll();
			instances.addAll(loader.getInstances());
			spaceHash=new SpaceHash(roomWidth,roomHeight);
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	/**
	 * Sets the instance for the view to follow.
	 * @param i - the instance to follow.
	 */
	public void setViewObject(Instance i)
	{
		viewObject=i;
	}
	
	/**
	 * Finds the object that is the focus of the view.
	 * @return the object that the view is following, or null if
	 * the view isn't following an object
	 */
	public Instance getViewObject()
	{
		return viewObject;
	}
	
	/**
	 * Offsets the view by a certain amount.
	 * @param xOffset - x amount to offset the view.
	 * @param yOffset - y amount to offset the view.
	 */
	public void setViewOffset(int xOffset,int yOffset)
	{
		viewXOffset=xOffset;
		viewYOffset=yOffset;
	}
	
	/**
	 * Sets the size of the view, but not the port on the screen. A
	 * view smaller than port will appear zoomed in, while a view
	 * larger than the port will look zoomed out.
	 * @param width - width of view.
	 * @param height - height of view.
	 */
	public void setViewSize(int width,int height)
	{
		viewWidth=width;
		viewHeight=height;
	}
	
	/**
	 * Sets the view port size. Since the default is the size of the window
	 * in the first place, you really shouldn't need to call this, but
	 * just in case you need to do so, there is this method.
	 * @param width - width of the port.
	 * @param height - height of the port.
	 */
	public void setPortSize(int width,int height)
	{
		portWidth=width;
		portHeight=height;
	}

	public int getID()
	{
		return ID;
	}
}